package com.xyxy.platform.admin.common.aspect;

import com.xyxy.platform.modules.core.web.response.BindingError;
import com.xyxy.platform.modules.core.web.response.ResponseMessage;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;

import javax.validation.Valid;
import javax.validation.Validator;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 参数绑定, 自动响应错误 json 切面
 * 使用了 @ResponseBody 注解, 并且返回值为 com.xyxy.platform.modules.core.web.response.ResponseMessage<?>
 * 的 controller, 无需重复判断 BindingResult result.hasErrors() 进行判断.
 * 但是对于普通非 ajax 请求的依然需要根据业务自行判断.
 * Created by VirtueCai on 15/11/20.
 */
@Aspect
@Component
public class ValidAspect {

    @Autowired
    private Validator validator;

    @Around("@annotation(org.springframework.web.bind.annotation.ResponseBody)")
    public Object doTest(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        if (!ResponseMessage.class.equals(method.getReturnType())) {
            pjp.proceed();
        }
        Object[] args = pjp.getArgs();
        Annotation[][] annotations = method.getParameterAnnotations();
        for (int i = 0; i < annotations.length; i++) {
            if (!hasValidAnnotation(annotations[i])) {
                continue;
            }
            if (!(i < annotations.length - 1 && args[i + 1] instanceof BindingResult)) {
                //验证对象后面没有跟bindingResult,事实上如果没有应该到不了这一步
                continue;
            }
            BindingResult result = (BindingResult) args[i + 1];
            if (result.hasErrors()) {
                ResponseMessage ajaxResponse = new ResponseMessage();
                ajaxResponse.setSuccess(false);
                ajaxResponse.setHasErrors(true);
                ajaxResponse.setResult(processErrors(result));
                return ajaxResponse;
            }
        }
        return pjp.proceed();
    }

    private boolean hasValidAnnotation(Annotation[] annotations) {
        if (annotations == null) {
            return false;
        }
        for (Annotation annotation : annotations) {
            if (annotation instanceof Valid) {
                return true;
            }
        }
        return false;
    }

    /**
     * 封装简单数据绑定错误信息.
     * @param result
     * @return
     */
    private List<BindingError> processErrors(BindingResult result) {
        if (result != null && result.hasErrors()) {
            List<BindingError> list = new ArrayList<>();
            for (ObjectError error : result.getAllErrors()) {
                FieldError fieldError = (FieldError)error;
                BindingError be = new BindingError();
                be.setMessage(fieldError.getDefaultMessage());
                be.setObjectName(fieldError.getObjectName());
                be.setField(fieldError.getField());
                list.add(be);
            }
            return list;
        }
        return null;
    }
}